package com.newflypig.jblog.dao.hibernate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;

import com.newflypig.jblog.util.BlogConfig;
import com.newflypig.jblog.dao.BaseDAO;

/**
 *  公用HibernateDAO，封装常用方法
 *	@author newflypig
 *	time：2015年11月23日
 *
 */
@SuppressWarnings("unchecked")
public abstract class BaseHibernateDAO<T extends Serializable> implements BaseDAO<T> {
	@Resource(name="sessionFactory")	//注入Spring提供的sessionFactory
	private SessionFactory sessionFactory;
	//子类实际类
	protected Class<T> entityClazz;
	protected final Log log = LogFactory.getLog(getClass());
	
	public BaseHibernateDAO(){
		this.resovleClazzInfo();
	}
	
	private void resovleClazzInfo() {
        Type genType = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        entityClazz = (Class<T>) params[0];
    }
	
	public Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}
	
	public void save(T entity){
		if(entity==null){
			log.info("None "+entityClazz.getSimpleName()+" object saved,it is NULL");
			return;
		}
		try{
			this.getSession().save(entity);
			log.info("Insert a " +entityClazz.getSimpleName()+ " object successful");
		}catch(RuntimeException re){
			log.error("Occur an Error when Insert a " +entityClazz.getSimpleName()+ " object"+re);
			throw re;
		}
	}
	
	public void delete(T entity){
		try{
			this.getSession().delete(entity);
			log.info("Delete a " +entityClazz.getSimpleName()+ " object successful");
		}catch(RuntimeException re){
			log.error("Occur an Error when Delete a " +entityClazz.getSimpleName()+ " object"+re);
			throw re;
		}
	}
	
	public void deleteById(Integer id){
		try{
			T entity=this.findById(id);
			if(entity!=null){
				this.delete(entity);
				log.info("Delete a " +entityClazz.getSimpleName()+ " object which id="+id+" successful");
			}else
				log.info("Can't find any " +entityClazz.getSimpleName()+ " object which id="+id);
		}catch(RuntimeException re){
			log.error("Occur an Error when Delete a " + entityClazz.getSimpleName() + " object"+re);
			throw re;
		}
	}
	
	public T findById(Integer id){
		try{
			T entity=(T)this.getSession().get(entityClazz, id);
			if(entity!=null)
				log.info("Find a " +entityClazz.getSimpleName()+ " object successful");
			else
				log.info("Find none " +entityClazz.getSimpleName()+ " object where id is:"+id);
			return entity;
		}catch(RuntimeException re){
			log.error("Occur an Error when try to Find a " +entityClazz.getSimpleName()+ " object,it's id is:"+id,re);
			throw re;
		}
	}
	
	public List<T> findAll(){
		try {
			String queryString = "from " +entityClazz.getSimpleName();
			Query queryObject = getSession().createQuery(queryString);
			List<T> list=queryObject.list();
			if(list.size()!=0)
				log.info("Find All " +entityClazz.getSimpleName()+ " objects");
			else
				log.info("Find none " +entityClazz.getSimpleName()+ " object");
			return list;
		} catch (RuntimeException re) {
			log.error("Occur an Error when try to Find All " +entityClazz.getSimpleName()+ " objects", re);
			throw re;
		}
	}
	
	public void update(T entity){
		if(entity==null){
			log.info("None "+entityClazz.getSimpleName()+" object updated,it is NULL");
			return;
		}
		try {
			this.getSession().update(entity);
			log.info("Update a " +entityClazz.getSimpleName()+ " object successful");
		} catch (RuntimeException re) {
			log.error("Occur an Error when try to Update a " +entityClazz.getSimpleName()+ " object", re);
			throw re;
		}
	}
	
	public List<T> findByHQL(String hql){
		try {
			Query queryObject = getSession().createQuery(hql);
			if(queryObject.list().size()!=0)
				log.info("Find All " +entityClazz.getSimpleName()+ " objects");
			else
				log.info("Find none " +entityClazz.getSimpleName()+ " object");
			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("Occur an Error when try to Find " +entityClazz.getSimpleName()+ " objects by hql:/n" +hql, re);
			throw re;
		}
	}
	
	@Override
	public List<T> findPager(DetachedCriteria dc,int page){
		int numPerPage=BlogConfig.getNumberPerPage(entityClazz);
		try {					
			List<T> list=dc.getExecutableCriteria(this.getSession())
							.setFirstResult((page-1)*numPerPage)
							.setMaxResults(numPerPage).list();
			if(list.size()!=0)
				log.info("Find " +entityClazz.getSimpleName()+ " objects at page "+page);
			else
				log.info("Find none " +entityClazz.getSimpleName()+ " object at page "+page);
			return list;
		} catch (RuntimeException re) {
			log.error("Occur an Error when try to Find " +entityClazz.getSimpleName()+ " objects at page "+page, re);
			throw re;
		}
	}
	
	@Override
	public long getCount(DetachedCriteria dc) {
		dc.setProjection(Projections.rowCount());
		long count=(long)(dc.getExecutableCriteria(this.getSession()).uniqueResult());
		return count;
	}
}